﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LPLib
{
    public static class Writer<T>
    {
        private static int qScoreOffset = -33;
        public static int QScoreOffset
        {
            get { return qScoreOffset; }
            set { qScoreOffset = value; }
        }

        public static string WriteFasta(MonomerEncoding<T> encoding, PolymerCollection<T> pc)
        {
            StringBuilder s = new StringBuilder();
            foreach (Polymer<T> p in pc)
            {
                s.AppendLine(">" + p.Name);
                foreach (T monomer in p.Seq)
                {
                    s.Append(encoding.ToChar(monomer).ToString());
                }
                s.AppendLine();
            }
            return s.ToString();
        }

        public static string WriteFasta(MonomerEncoding<T> encoding, Polymer<T> p)
        {
            StringBuilder s = new StringBuilder();
            s.AppendLine(">" + p.Name);
            foreach (T monomer in p.Seq)
            {
                s.Append(encoding.ToChar(monomer).ToString());
            }
            return s.ToString();
        }

        public static string WriteFasta(MonomerEncoding<T> encoding, PolymerPair<T, T> pp)
        {
            int indexLength = pp.Index.GetLength(0);
            StringBuilder s = new StringBuilder();

            s.AppendLine(">" + pp.Polymer0.Name);
            for (int k = 0; k < indexLength; k++)
            {
                if (pp.Index[k][0] >= 0)
                    s.Append(encoding.ToChar(pp.Polymer0.Seq[pp.Index[k][0]]).ToString());
                else
                    s.Append('-');
            }
            s.AppendLine();

            s.Append(">" + pp.Polymer1.Name + "\n");
            for (int k = 0; k < indexLength; k++)
            {
                if (pp.Index[k][1] >= 0)
                    s.Append(encoding.ToChar(pp.Polymer1.Seq[pp.Index[k][1]]).ToString());
                else
                    s.Append('-');
            }
            s.AppendLine();
            return s.ToString();
        }

        public static string WriteFasta(PolymerPair<double[], double[]> pp)
        {
            char[] seq1 = TypeConverter.ToChars(pp.Polymer0.Seq);
            char[] seq2 = TypeConverter.ToChars(pp.Polymer1.Seq);

            int indexLength = pp.Index.GetLength(0);
            StringBuilder s = new StringBuilder();

            s.AppendLine(">" + pp.Polymer0.Name);
            for (int k = 0; k < indexLength; k++)
            {
                if (pp.Index[k][0] >= 0)
                    s.Append(seq1[pp.Index[k][0]]);
                else
                    s.Append('-');
            }
            s.AppendLine();

            s.Append(">" + pp.Polymer1.Name + "\n");
            for (int k = 0; k < indexLength; k++)
            {
                if (pp.Index[k][1] >= 0)
                    s.Append(seq2[pp.Index[k][1]]);
                else
                    s.Append('-');
            }
            s.AppendLine();
            return s.ToString();
        }

        public static string WriteFasta(Trefoil trefoil)
        {
            char[][] seqs = new char[4][];
            seqs[0] = TypeConverter.ToChars(trefoil.Polymers[0].Seq);
            seqs[1] = TypeConverter.ToChars(trefoil.Polymers[1].Seq);
            if (trefoil.MRCANormalized == null)
            {
                if (trefoil.MRCALikelihoodSequence ==null)
                {
                    seqs[2] = new char[trefoil.Index.Length];
                }
                else
                {
                    trefoil.MRCANormalized = Polymer<double[]>.Normalize(trefoil.MRCALikelihoodSequence);
                    seqs[2] = TypeConverter.ToChars(trefoil.MRCANormalized.Seq);
                }
            }
            else
            {
                seqs[2] = TypeConverter.ToChars(trefoil.MRCANormalized.Seq);
            }
            seqs[3] = TypeConverter.ToChars(trefoil.RootNormalized.Seq);

            string[] names = new string[4];
            names[0] = trefoil.Polymers[0].Name;
            names[1] = trefoil.Polymers[1].Name;
            if (trefoil.MRCALikelihoodSequence != null)
            {
                names[2] = trefoil.MRCALikelihoodSequence.Name;
            }
            else
            {
                names[2] = "SKIP";
            }
            names[3] = trefoil.RootLikelihoodSequence.Name;

            StringBuilder s = new StringBuilder();

            for (int i = 0; i < 4; i++ )
            {
                if (names[i] == "SKIP")
                    continue;

                s.AppendLine(">" + names[i]);
                for (int k = 0; k < trefoil.AlignmentLength; k++)
                {
                    if (trefoil.Index[k][i] >= 0)
                        s.Append(seqs[i][trefoil.Index[k][i]]);
                    else
                        s.Append('-');
                }
                s.AppendLine();
            }
            return s.ToString();
        }

        /// <summary>
        /// Reads a polynucleotide collection from a FASTQ file, returning it as a Dictionary of polynucs keyed 
        /// by auto-generated IDs.
        /// </summary>
        /// <param name="filename">The name of the FASTA file.</param>
        /// <returns>A Dictionary of polynucs keyed by auto-generated IDs.</returns>
        public static string WriteFastq(MonomerEncoding<T> encoding, PolymerCollection<T> pc)
        {
            // FASTQ format has the form:
            // 
            // @HWI-EAS209_0006_FC706VJ:5:58:5894:21141#ATCACG/1
            // TTAATTGGTAAATAAATCTCCTAATAGCTTAGATNTTACCTTNNNNNNNNNNTAGTTTCTTGAGATTTGTTGGGGGAGACATTTTTGTGATTGCCTTGAT
            // +HWI-EAS209_0006_FC706VJ:5:58:5894:21141#ATCACG/1
            // efcfffffcfeefffcffffffddf`feed]`]_Ba_^__[YBBBBBBBBBBRTT\]][]dddd`ddd^dddadd^BBBBBBBBBBBBBBBBBBBBBBBB
            //
            // the "@" denotes the beggining of a new entry. Its title fills the rest of that line
            // the next line contains the bases of the sequence
            // the next line begins with "+" and may repeat the title
            // the fourth and final line contains the quality score, given by the byte equivalent of the ascii character
            // with some offset. The offset depends on the sequencing platform used

            StringBuilder sb = new StringBuilder();
            
            foreach (Polymer<T> p in pc)
            {
                sb.AppendLine("@" + p.Name);
                sb.AppendLine(encoding.ToString(p.Seq));
                sb.AppendLine("+" + p.Name);
                foreach (byte q in p.Qual)
                {
                    sb.Append((char)(q - qScoreOffset));
                }
                sb.AppendLine();
            }
            return sb.ToString();
        }

        public static string WriteNPMF(Polymer<double[]> p)
        {
            StringBuilder s = new StringBuilder();
            s.Append(p.Name);
            for (int iPos = 0; iPos < p.Seq.Length; iPos++)
            {
                s.Append("\t" + (iPos + 1));
            }
            s.AppendLine();
            for (int iNuc = 0; iNuc < 5; iNuc++ )
            {
                s.Append(SymbolSets.PureNucleotides[iNuc]);
                for (int iPos = 0; iPos < p.Seq.Length; iPos++)
                {
                    s.Append("\t" + p.Seq[iPos][iNuc]);
                }
                s.AppendLine();
            }
            s.Append("Error");
            for (int iPos = 0; iPos < p.Seq.Length; iPos++)
            {
                double error = 0;
                for (int iNuc = 0; iNuc < 5; iNuc++ )
                {
                    error -= p.Seq[iPos][iNuc] * p.Seq[iPos][iNuc];
                }
                error += 1;
                s.Append("\t" + error);
            }
            s.AppendLine();
            return s.ToString();
        }

        public static string WriteNPMF(PolymerCollection<double[]> pc)
        {
            StringBuilder s = new StringBuilder();
            foreach (Polymer<double[]> p in pc)
            {
                s.Append(WriteNPMF(p));
            }
            return s.ToString();
        }

        public static string WriteNPMF(PolymerPair<double[], double[]> pair)
        {
            StringBuilder s = new StringBuilder();
            s.Append(pair.Polymer0.Name);
            for (int k = 0; k < pair.Index.Length; k++)
            {
                s.Append("\t" + (k + 1));
            }
            s.AppendLine();
            for (int iNuc = 0; iNuc < 5; iNuc++)
            {
                s.Append(SymbolSets.PureNucleotides[iNuc]);
                for (int k = 0; k < pair.Index.Length; k++)
                {
                    if (pair.Index[k][0] > -1)
                    {
                        s.Append("\t" + pair.Polymer0.Seq[pair.Index[k][0]][iNuc]);
                    }
                    else
                    {
                        s.Append("\t" + (iNuc == 4 ? "1" : "0"));
                    }
                }
                s.AppendLine();
            }
            s.Append("Error");
            for (int k = 0; k < pair.Index.Length; k++)
            {
                if (pair.Index[k][0] > -1)
                {
                    double error = 1;
                    for (int iNuc = 0; iNuc < 5; iNuc++)
                    {
                        error -= Math.Pow(pair.Polymer0.Seq[pair.Index[k][0]][iNuc], 2);
                    }
                    s.Append("\t" + error);
                }
                else
                {
                    s.Append("\t0");
                }
            }
            s.AppendLine();

            s.Append(pair.Polymer1.Name);
            for (int k = 0; k < pair.Index.Length; k++)
            {
                s.Append("\t" + (k + 1));
            }
            s.AppendLine();
            for (int iNuc = 0; iNuc < 5; iNuc++)
            {
                s.Append(SymbolSets.PureNucleotides[iNuc]);
                for (int k = 0; k < pair.Index.Length; k++)
                {
                    if (pair.Index[k][1] > -1)
                    {
                        s.Append("\t" + pair.Polymer1.Seq[pair.Index[k][1]][iNuc]);
                    }
                    else
                    {
                        s.Append("\t" + (iNuc == 4 ? "1" : "0"));
                    }
                }
                s.AppendLine();
            }
            s.Append("Error");
            for (int k = 0; k < pair.Index.Length; k++)
            {
                if (pair.Index[k][1] > -1)
                {
                    double error = 1;
                    for (int iNuc = 0; iNuc < 5; iNuc++)
                    {
                        error -= Math.Pow(pair.Polymer1.Seq[pair.Index[k][1]][iNuc], 2);
                    }
                    s.Append("\t" + error);
                }
                else
                {
                    s.Append("\t0");
                }
            }
            s.AppendLine();

            return s.ToString();
        }

    }
}
